#ifndef __QTCH_LOG_H__
#define __QTCH_LOG_H__

#include <stdint.h>
#include <string>
#include <memory>
#include <sstream>
#include <ostream>
#include <iostream>
#include <vector>
#include <fstream>
#include <map>
#include <time.h>

#include "singleton.h"
#include "util.h"
#include "thread.h"


#define QTCH_LOG_LEVEL(logger,level) \
if(logger->getLevel() <= level) \
        qtch::LogEventWrap(qtch::LogEvent::ptr(new qtch::LogEvent(logger,level \
        ,__FILE__,__LINE__,0,qtch::GetThreadId(),qtch::GetFiberId(),time(0),qtch::Thread::GetThreadName()))).getSS()

#define QTCH_LOG_DEBUG(logger) QTCH_LOG_LEVEL(logger,qtch::LogLevel::DEBUG)

#define QTCH_LOG_INFO(logger) QTCH_LOG_LEVEL(logger,qtch::LogLevel::INFO)

#define QTCH_LOG_WARN(logger) QTCH_LOG_LEVEL(logger,qtch::LogLevel::WARN)

#define QTCH_LOG_ERROR(logger) QTCH_LOG_LEVEL(logger,qtch::LogLevel::ERROR)

#define QTCH_LOG_FATAL(logger) QTCH_LOG_LEVEL(logger,qtch::LogLevel::FATAL)


#define QTCH_LOG_ROOT() qtch::LoggerMgr::GetInstance()->getRoot()

#define QTCH_LOG_NAME(name) qtch::LoggerMgr::GetInstance()->getLogger(name)



namespace qtch
{


class Logger;

class LogLevel
{

public:
    enum Level
    {
        UNKNOW = 0,
        DEBUG,
        INFO,
        WARN,
        ERROR,
        FATAL
    };

    static const char * ToString(LogLevel::Level level);

    static LogLevel::Level FromString(const std::string& str);

};

class StdoutLogColor{
public:
    enum Color{
        DEFAULT = 0,
        Blue,
        Green,
        Red,
        Yellow,
        White,
        Cyan,
        Magenta,
        BrightBlue,
        BrightGreen,
        BrightRed,
        BrightYellow,
        BrightWhite,
        BrightCyan,
        BrightMagenta,
    };
    const std::string getColorString(LogLevel::Level level);
    const std::string getColorCode(Color color);
    const std::string getColorCode(LogLevel::Level level);
    void setColorFromString(LogLevel::Level level,const std::string& str);

private:
    StdoutLogColor::Color m_color_debug = StdoutLogColor::Color::DEFAULT;
    StdoutLogColor::Color m_color_info = StdoutLogColor::Color::DEFAULT;
    StdoutLogColor::Color m_color_warn = StdoutLogColor::Color::Yellow;
    StdoutLogColor::Color m_color_error = StdoutLogColor::Color::Red;
    StdoutLogColor::Color m_color_fatal = StdoutLogColor::Color::Red;
};

class LogEvent{

public:
    typedef std::shared_ptr<LogEvent> ptr;

    LogEvent(std::shared_ptr<Logger> logger, LogLevel::Level level, 
                const char * fileName, int32_t line, uint32_t elapse, 
                uint32_t threadId, uint32_t fiberId,
                uint64_t time, const std::string& threadName);

    const char * getFileName() const {return m_file;}

    int32_t getLine() const {return m_line;}

    int32_t getElapse() const {return  m_elapse;}

    uint32_t getThreadId() const {return m_threadId;}

    uint32_t getFiberId() const {return m_fiberId;}

    uint64_t getTime() const {return m_time;}

    const std::string& getThreadName() const {return m_threadName;}

    std::stringstream& getSS() {return m_ss;}

    std::string getContent(){return m_ss.str();}

    std::shared_ptr<Logger> getLogger() const {return m_logger;}

    LogLevel::Level getLevel() const {return m_level;}



private:
    /// 日志器
    std::shared_ptr<Logger> m_logger;
    /// 日志等级
    LogLevel::Level m_level;
    // 文件名
    const char * m_file = nullptr;
    int32_t m_line = 0;
    // 程序启动开始到现在的毫秒数
    uint32_t m_elapse = 0;
    /// 线程ID
    uint32_t m_threadId = 0;
    /// 协程ID
    uint32_t m_fiberId = 0;
    /// 时间戳
    uint64_t m_time = 0;
    /// 线程名称
    std::string m_threadName;
    // 日志内容流
    std::stringstream m_ss;

};

class LogEventWrap{
public:
    LogEventWrap(LogEvent::ptr p);
    ~LogEventWrap();

    LogEvent::ptr getEvent() const {return m_event;}

    std::stringstream& getSS();

private:
    LogEvent::ptr m_event;
};

class LogFormatter{

public:
    typedef std::shared_ptr<LogFormatter> ptr;

    LogFormatter(const std::string& pattern);

    class FormatterItem{
    public:
        typedef std::shared_ptr<FormatterItem> ptr;

        ~FormatterItem(){};

        virtual void format(std::ostream& os,std::shared_ptr<Logger> logger,LogEvent::ptr event) = 0;


    };

    void init();

    bool isError() const {return m_error;}

    std::string format(std::shared_ptr<Logger> logger,LogEvent::ptr event);

    std::ostream& format(std::ostream& ifs,std::shared_ptr<Logger> logger,LogEvent::ptr event);



private:

    std::vector<FormatterItem::ptr> m_formatItem;

    std::string m_pattern;

    bool m_error = false;

};

class LogAppender{

public:

    typedef std::shared_ptr<LogAppender> ptr;

    typedef Mutex MutexType;

    LogLevel::Level getLevel() const {return m_level;}

    void setLevel(LogLevel::Level level){m_level = level;}

    LogFormatter::ptr getFomatter();

    void setFormatter(LogFormatter::ptr formatter);

    virtual void log(std::shared_ptr<Logger> logger, LogLevel::Level level,LogEvent::ptr event) = 0;

    bool hasFormatter() const {return m_hasFormatter;}
    
protected:
    MutexType m_mutex;

private:

    bool m_hasFormatter = false;

    LogFormatter::ptr m_formatter;

    LogLevel::Level m_level = LogLevel::DEBUG;



};


class StdoutLogAppender : public LogAppender{
public:
    typedef std::shared_ptr<StdoutLogAppender> ptr;
    StdoutLogAppender(StdoutLogColor color);
    void log(std::shared_ptr<Logger> logger, LogLevel::Level level,LogEvent::ptr event) override;
private:
    StdoutLogColor m_color;
};

class FileLogAppender : public LogAppender{
public:
    typedef std::shared_ptr<FileLogAppender> ptr;
    FileLogAppender(const std::string& fileName);
    void init();
    void log(std::shared_ptr<Logger> logger, LogLevel::Level level,LogEvent::ptr event) override;
    bool reopen();
    std::string getFileName();

    class LogFileNameFormatterItem{
        public:
            typedef std::shared_ptr<LogFileNameFormatterItem> ptr;
            ~LogFileNameFormatterItem(){}
            virtual void format(std::ostream& os) = 0;
    };


private:
    std::string m_originFileName;

    std::string m_fileName;

    std::ofstream m_fileSteam;

    uint64_t m_lastTime = 0;

    std::vector<LogFileNameFormatterItem::ptr> m_formatItem;

};

class Logger : public std::enable_shared_from_this<Logger>{
friend class LoggerManger;
public:
    typedef std::shared_ptr<Logger> ptr;

    typedef RWMutex MutexType;

    Logger(const std::string& name = "root");

    const std::string& getName() const {return m_name;}

    LogLevel::Level getLevel() const {return m_level;}

    void setLevel(LogLevel::Level level){m_level = level;}

    LogFormatter::ptr getFormatter() const {return m_formatter;}

    void setFormatter(LogFormatter::ptr formatter);

    void addAppender(LogAppender::ptr appender);

    void delAppender(LogAppender::ptr appender);

    void clearAppenders();

    void log(LogLevel::Level level,LogEvent::ptr event);

    void debug(LogEvent::ptr event);

    void info(LogEvent::ptr event);

    void warn(LogEvent::ptr event);

    void error(LogEvent::ptr event);

    void fatal(LogEvent::ptr event);

private:

    std::string m_name;

    LogLevel::Level m_level;

    std::vector<LogAppender::ptr> m_appender;

    LogFormatter::ptr m_formatter;

    Logger::ptr m_root;

    MutexType m_mutex;
};

class LoggerManger{
public:
    typedef std::shared_ptr<LoggerManger> ptr;

    typedef RWMutex MutexType;

    LoggerManger();
    void init();
    Logger::ptr getRoot() const {return m_root;}
    Logger::ptr getLogger(const std::string& name);
private:
    std::map<std::string, Logger::ptr> m_logger;
    Logger::ptr m_root;
    MutexType m_mutex;

};

typedef SingletonPtr<LoggerManger> LoggerMgr;

}

#endif